package model;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.List;
import java.util.ArrayList;
import java.util.Iterator;
import java.awt.Color;
import controller.SimulatorController;
import tool.Database;
import tool.Randomizer;
import tool.SplashDemo;

/**
 * A simple predator-prey simulator, based on a rectangular field
 * containing rabbits and foxes.
 * 
 * @author Team X
 * @version 1
 */
@SuppressWarnings("rawtypes")
public class Simulator extends AbstractModel implements Runnable
{
    // Constants representing configuration information for the simulation.
    
	// The default width for the grid.
    public static final int DEFAULT_WIDTH = 100;
    // The default depth of the grid.
    public static final int DEFAULT_DEPTH = 100;

    // Field Statistics
    public FieldStats stats;
    // A map for storing colors for participants in the simulation, Gerhard made
	private Map<Class, Color> colors;
    // Color used for objects that have no defined color.
    private static final Color UNKNOWN_COLOR = Color.gray;
    // Colors used for empty locations.
    public static final Color EMPTY_COLOR = Color.white;

    // List of actors in the field. (previously animals)
    private List<Actor> actors;
    // Extra field storing hunters.
    public ArrayList<Hunter> hunters;

    // The current state of the field.
    public Field field;
    // Depth of field
    public int fieldDepth = DEFAULT_DEPTH;
    // Height of field
    public int fieldWidth = DEFAULT_WIDTH;
    // The current step of the simulation.
    private int step;
    // Runnable yes/no
    private boolean run;
    
    /**
     * Construct a simulation field with default size.
     */
    public Simulator()
    {
        this(DEFAULT_DEPTH, DEFAULT_WIDTH);
    }
    
    /**
     * Create a simulation field with the given size.
     * @param depth Depth of the field. Must be greater than zero.
     * @param width Width of the field. Must be greater than zero.
     */
    public Simulator(int depth, int width)
    {
    	// Set dimensions
        this.fieldDepth = depth;
        this.fieldWidth = width;
        
        // Unless dimensions are crap
        if(width <= 0 || depth <= 0) setDefaultSimulationDimensions();

        // Instanciate stuff
        step = 0;
        stats = new FieldStats();
        field = new Field(depth, width);
        actors = new ArrayList<Actor>();
        hunters = getHunters();
        //hunters = new ArrayList<Hunter>();
        colors = new LinkedHashMap<Class, Color>();

        // Set Actor and field colours
        setColor(Rabbit.class, Color.orange);
        setColor(Fox.class, Color.blue);
        setColor(Hunter.class, Color.black);
        setColor(Redfox.class, Color.red);
        setColor(Grass.class, Color.green);
        
        // Setup a valid starting point.
        reset();
    }
    
    private void setDefaultSimulationDimensions()
    {
    	this.fieldDepth = DEFAULT_DEPTH;
        this.fieldWidth = DEFAULT_WIDTH;
    }
    
    /**
     * Run the simulation from its current state for the given number of steps.
     * Stop before the given number of steps if it ceases to be viable.
     * @param numSteps The number of steps to run for.
     */
    public void simulate(int numSteps)
    {
    	// TODO && isViable(field)
        for(int step = 1; step <= numSteps; step++) {
            simulateOneStep();
        }
    }
    
    /**
     * Run the simulation from its current state for a single step.
     * Iterate over the whole field updating the state of each
     * fox and rabbit.
     */
    public void simulateOneStep()
    {
        step++;
        
        // Gras laten groeien
    	field.growGrass(field);

        // Provide space for newborn animals.
        List<Animal> newAnimals = new ArrayList<Animal>();        

        // Let all actors act.
        for(Iterator<Actor> it = actors.iterator(); it.hasNext(); ) 
        {
            Actor actor = it.next();
            
            // Animal/Hunter switch
            if(actor instanceof Animal)
            {
            	Animal animal = (Animal) actor;
            	animal.act(newAnimals);
                
            	if(! animal.isAlive()) 
                it.remove();
            } 
            else if (actor instanceof Hunter)
            {
            	Hunter hunter = (Hunter) actor; 
            	hunter.act();
            }
        }
               
        // Add all actors
        actors.addAll(newAnimals);

        // Update status
        showStatus(field);
    }
        
    /**
     * Reset the simulation to a starting position.
     */
    public void reset()
    {
        step = 0;
        actors.clear();
        populate();
        
        // Show the starting state in the view.
        showStatus(field);
    }
    
    /** Adds hunters to the field */
    public Hunter inviteHunter(Field field, Location location)
    {
    	for(Hunter hunter : this.hunters)
    	{
    		if(hunter.isAlive() && !hunter.isPlaced())
    		{
    			hunter.setLocation(location);
    			return hunter;
    		}
    	}
    	
    	return null;
    }
    
    /**
     * Randomly populate the field with foxes, rabbits.
     */
    private void populate()
    {
    	// Random instantieren
        Random rand = Randomizer.getRandom();
        
        // Hunters mogen maximaal tot de grootte van de array
        int amount_hunters = 0;
        int maximum_hunters = 5;

        // Veld leegmaken
        field.clear();
        
        // Elk veld 
        for(int row = 0; row < field.getDepth(); row++) 
        {
            for(int col = 0; col < field.getWidth(); col++) 
            {
            	// Plaats aanmaken (?)
            	Location location = new Location(row, col);
            	
            	// Fox
                if(rand.nextDouble() <= Fox.breeding_probability) 
                {
                    Fox fox = new Fox(true, field, location);
                    actors.add(fox);
                }
                // Rabbit
                else if(rand.nextDouble() <= Rabbit.breeding_probability) 
                {
                    Rabbit rabbit = new Rabbit(true, field, location);
                    actors.add(rabbit);
                }
                // Red Fox
                else if(rand.nextDouble() <= Redfox.breeding_probability) 
                {
                    Redfox redfox = new Redfox(true, field, location);
                    actors.add(redfox);
                }
                // Hunters
                else if((amount_hunters < maximum_hunters) && (rand.nextDouble() <= Hunter.creation_probability))
                {
                	//Hunter hunter = new Hunter(true, field, location);
                	//for(int i=0;i<hunters.size();i++)
                	//{
                		//Hunter hunter = inviteHunter(field, location);
                	//}
                	
                	Hunter hunter = inviteHunter(field, location);
                	
                	/*switch(amount_hunters)
                	{
                		case 0 : hunter.name = "Simon";	break;
                		case 1 : hunter.name = "Gerhard"; break;
                		case 2 : hunter.name = "Jort"; break;
                		case 3 : hunter.name = "Jeroen"; break;
                		case 4 : hunter.name = "Koen"; break;
                		default : hunter.name = "Unknown"; break;
                	}*/
                	
                	amount_hunters += 1;
                	actors.add(hunter);
                	hunters.add(hunter);
                } 
                // Grass 
                else
                {
                	// Set random hoogte 0-4
                	field.setGrassHeight(rand.nextInt(4), row, col);
                	
                	// Als er gras is maak groen aan
                	if(field.getGrassHeight(row, col) > 0)
                	{ new Grass(field, location); }
                	
                }
            }
        }
    }
    
    public Field getField()
    {
    	return field;
    }
    
    /**
     * Returns the array with hunters
     */
    public ArrayList<Hunter> getHunters()
    {
    	this.hunters = new ArrayList<Hunter>();
    	
    	ResultSet result = Database.query("SELECT * FROM VK.HUNTERS");
    	
    	try {
			while(result.next())
			{
				Hunter hunter = new Hunter(field);
				hunter.name = result.getString("NAME");
				this.hunters.add(hunter);
			}
		} 
    	catch(SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    	
    	return hunters; 
    }
    
    public int getHunterAmount()
    {
    	int getal = hunters.size();
    	return getal;
    }
    
    /**
     * Define a color to be used for a given class of animal.
     * @param animalClass The animal's Class object.
     * @param color The color to be used for the given class.
     */
    @SuppressWarnings("rawtypes")
	public void setColor(Class actorClass, Color color)
    {
        colors.put(actorClass, color);
    }

    /**
     * @return The color to be used for a given class of animal.
     */
    @SuppressWarnings("rawtypes")
	public Color getColor(Class animalClass)
    {
        Color col = colors.get(animalClass);
        if(col == null) 
        {
            // no color defined for this class
            return UNKNOWN_COLOR;
        }
        else 
        {
            return col;
        }
    }   
    
    /**
     * Counting actors in the field
     * @return List<Actor> Returns an array of int's.
     */
    public int[] getActors()
    {
    	int rabbitCount = 0;
    	int foxCount = 0;
    	int redfoxCount = 0;
    	int hunterCount = 0;
    	
    	for (Actor actor : actors)
    	{
    		if (actor instanceof Rabbit) rabbitCount ++;
    		if (actor instanceof Fox) foxCount ++;	
    		if (actor instanceof Redfox) redfoxCount ++;
    		if (actor instanceof Hunter) hunterCount ++;
    	}
    	
    	int[] actorCountArray = new int[5];
    	actorCountArray[0] = actors.size();
    	actorCountArray[1] = rabbitCount;
    	actorCountArray[2] = foxCount;
    	actorCountArray[3] = redfoxCount;
    	actorCountArray[4] = hunterCount;
    	
    	return actorCountArray;
    }
    

    public void start() 
    {
    	new Thread(this).start();
    }
	
	public void stop()
	{
		run=false;
	}
	
	public void run() 
	{
		run=true;
		while(run) {
			simulateOneStep();
			try {
				Thread.sleep(100);
			} catch (Exception e) {} 
		}
	}
	
    /**
     * Determine whether the simulation should continue to run.
     * @return true If there is more than one species alive.
     */
    public boolean isViable(Field field)
    {
        return stats.isViable(field);
    }
    
	/**
	 * Show the current status of the field.
	 * @param field The field whose status is to be displayed.
	 */
	public void showStatus(Field field)
	{
	    stats.reset();
	
	    for(int row = 0; row < field.getDepth(); row++) 
	    {
	        for(int col = 0; col < field.getWidth(); col++) 
	        {
	            Object animal = field.getObjectAt(row, col);
	            if(animal != null) 
	            {
	                stats.incrementCount(animal.getClass());
	            }  
	        }
	    }
	    
	    stats.countFinished();
	    
	    notifyViews();
	}
}
    
  


